import { isSameNode } from './patch';
import patchNode from './patchNode';
import createElem from './createElem';

/**
 * 当新旧节点都有子节点时进行的精细化比较，
 * 使用两对双指针进行比较
 * 1.新前-旧前
 * 2.新后-旧后
 * 3.新后-旧前
 * 4.新前-旧后
 *
 * @param {Element} parentElm
 * @param {vnode[]} oldCh 旧节点的子节点
 * @param {vnode[]} newCh 新节点的子节点
 */
function updateChildren(parentElm, oldCh, newCh) {
  // 旧前
  let oldStartIdx = 0;
  // 旧后
  let oldEndIdx = oldCh.length - 1;
  // 新前
  let newStartIdx = 0;
  // 新后
  let newEndIdx = newCh.length - 1;
  // 旧前节点
  let oldStartVnode = oldCh[oldStartIdx];
  // 旧后节点
  let oldEndVnode = oldCh[oldEndIdx];
  // 新前节点
  let newStartVnode = newCh[newStartIdx];
  // 新后节点
  let newEndVnode = newCh[newEndIdx];

  let keyMap = null;

  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {

    // 跳过已经加上undefuned标记的项
    if (!oldStartVnode) {
      oldStartVnode = oldCh[++oldStartIdx];
    } else if (!oldEndVnode) {
      oldEndVnode = oldCh[--oldEndIdx];
    } else if (!newStartVnode) {
      newStartVnode = newCh[++newStartIdx];
    } else if (!newEndVnode) {
      newEndVnode = newCh[--newEndIdx];
    }

    // 1.新前-旧前  比较  如果是同一个节点，交由patchNode处理
    if (isSameNode(oldStartVnode, newStartVnode)) {
      patchNode(oldStartVnode, newStartVnode);
      // 移动指针位置
      oldStartVnode = oldCh[++oldStartIdx];
      newStartVnode = newCh[++newStartIdx];
    } else if (isSameNode(oldEndVnode, newEndVnode)) {
      // 2.新后-旧后
      patchNode(oldEndVnode, newEndVnode);
      // 移动指针位置
      oldEndVnode = oldCh[--oldEndIdx];
      newEndVnode = newCh[--newEndIdx];
    } else if (isSameNode(oldStartVnode, newEndVnode)) {
      // 3.新后-旧前
      patchNode(oldStartVnode, newEndVnode);
      // 移动新前指向的这个节点到老节点的旧后的后面
      // 只要插入一个已经在dom树上的节点，这个节点就会被移动到插入到位置
      parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
      // 移动指针位置
      oldStartVnode = oldCh[++oldStartIdx];
      newEndVnode = newCh[--newEndIdx];
    } else if (isSameNode(oldEndVnode, newStartVnode)) {
      // 4.新前-旧后
      patchNode(oldEndVnode, newStartVnode);
      // 移动新前指向的这个节点到老节点的旧前的前面
      parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
      // 移动指针位置
      oldEndVnode = oldCh[--oldEndIdx];
      newStartVnode = newCh[++newStartIdx];
    } else {
      // 都没有匹配到
      // 缓存key与索引值的映射
      if (!keyMap) {
        keyMap = {};
        for (let i = oldStartIdx; i < oldEndIdx; i++) {
          const key = oldCh[i].key;
          if (key !== undefined) {
            keyMap[key] = i;
          }
        }
      }
      // 寻找当前这项(newStartIdx)这项在keyMap中的位置序号
      const idxInOld = keyMap[newStartVnode.key];

      if (idxInOld === undefined) {
        // 没有在缓存中找到内容，说明当前项是全新的项，直接新增
        parentElm.insertBefore(createElem(newStartVnode), oldStartVnode.elm);
      } else {
        // 不是全新的项，需要移动
        const elmToMove = oldCh[idxInOld];

        if (elmToMove) {
          patchNode(elmToMove, newStartVnode);
          // 标记，表示这一项已经处理过了
          oldCh[idxInOld] = undefined;
          parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
        }
      }
      // 指针下移，只移动新前
      newStartVnode = newCh[++newStartIdx];
    }
  }

  // 继续判断是否有剩余节点
  if (oldStartIdx <= oldEndIdx) {
    // 旧节点有剩余节点，表示新节点删除了某些节点
    for (let i = oldStartIdx; i <= oldEndIdx; i++) {
      if (oldCh[i]) {
        parentElm.removeChild(oldCh[i].elm);
      }
    }
  } else if (newStartIdx <= newEndIdx) {
    // 新节点有剩余节点，表示新节点新增了某些节点
    for (let i = newStartIdx; i <= newEndIdx; i++) {
      parentElm.insertBefore(createElem(newCh[i]), oldCh[oldStartIdx].elm);
    }
  }
}

export default updateChildren;
